﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;


namespace ClientLib
{
    public class EncryptUtil
    {
        public EncryptUtil()
        {

        }

        # region Hash

        /// <summary>
        /// This method uses the username as "salt" value to calculate the password hash
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public string PasswordHash(string username, string password)
        {
            string pwordhash = "";

            // create hash value of username+password
            string concatStr = username + password;
            HashAlgorithm hash = new SHA256Managed();
            byte[] clearArray = System.Text.ASCIIEncoding.ASCII.GetBytes(concatStr);
            byte[] hashArray = hash.ComputeHash(clearArray);
            string hashStr = Convert.ToBase64String(hashArray);
            pwordhash = hashStr;

            return pwordhash;
        }

        /// <summary>
        /// creates a 32 byte string from passphrase to be used as symmetric encryption key
        /// </summary>
        /// <param name="passphrase"></param>
        /// <returns></returns>
        public string GetKeyFromPassphrase(string passphrase)
        {
            string symmKey = "";

            string tmp = passphrase;
            while (tmp.Length < 32)
            {
                tmp += tmp;
            }
            symmKey = tmp.Substring(0, 32);

            return symmKey;
        }

        # endregion


        # region Asymmetric

        /// <summary>
        /// encrypts a symmetric key using an RSA public key
        /// </summary>
        /// <param name="clearText"></param>
        /// <param name="pubKey"></param>
        /// <returns>encrypted symmetric key as a string</returns>
        public byte[] RSAEncryptKey(byte[] clearText, string pubKey)
        {
            byte[] cipherKey = StringToByte("");
            
                RSACryptoServiceProvider Rsa = new RSACryptoServiceProvider();
                if (pubKey != null && pubKey != "")
                {
                    // read the private key from the xml key file
                    Rsa.FromXmlString(pubKey);
                    // encrypt the symmkey
                    cipherKey = Rsa.Encrypt(clearText, false);
                }

            return cipherKey;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="clearText"></param>
        /// <param name="pubKey"></param>
        /// <returns></returns>
        public string RSAEncryptKey(string clearText, string pubKey)
        {
            byte[] cipherKey = StringToByte("");
            byte[] clearByte = StringToByte(clearText);

            RSACryptoServiceProvider Rsa = new RSACryptoServiceProvider();
            if (pubKey != null && pubKey != "")
            {
                // read the private key from the xml key file
                Rsa.FromXmlString(pubKey);
                // encrypt the symmkey
                cipherKey = Rsa.Encrypt(clearByte, false);
            }

            return ByteToBase64String(cipherKey);
        }

        # endregion


        # region Symmetric

        /// <summary>
        /// builds a 32-byte symmetric key from a GUID
        /// </summary>
        /// <returns>32-byte string</returns>
        public string CreateSymmKey()
        {
            string symkey = "";

            // create new guid string as starting value, strip out hyphens from guid, converting 36 byte guid to 32 byte string
            string GUID = System.Guid.NewGuid().ToString().Replace("-", "");
            symkey = GUID.Substring(0, 32);
            
            return symkey;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="cryptoText"></param>
        /// <param name="symmKey"></param>
        /// <returns></returns>
        public string DecryptString2(string cryptoText, string symmKey)
        {
            string decryptedData = "";

            RijndaelManaged rm = null;
            MemoryStream memoryStream = null;
            CryptoStream cryptoStream = null;

            try
            {
                string salt = symmKey.Substring(0, 16);
                //Generate a Key based on a Password, Salt and HMACSHA1 pseudo-random number generator 
                Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(symmKey, Encoding.Unicode.GetBytes(salt));

                //Create AES algorithm with 256 bit key and 128-bit block size 
                rm = new RijndaelManaged();
                rm.KeySize = 256;
                rm.BlockSize = 128;
                rm.Key = rfc2898.GetBytes(rm.KeySize / 8);
                rm.IV = rfc2898.GetBytes(rm.BlockSize / 8);

                //Create Memory and Crypto Streams 
                memoryStream = new MemoryStream();
                cryptoStream = new CryptoStream(memoryStream, rm.CreateDecryptor(), CryptoStreamMode.Write);

                //Decrypt Data 
                byte[] data = Convert.FromBase64String(cryptoText);
                cryptoStream.Write(data, 0, data.Length);
                cryptoStream.FlushFinalBlock();

                //Return Decrypted String 
                byte[] decryptBytes = memoryStream.ToArray();
                decryptedData = Encoding.Unicode.GetString(decryptBytes, 0, decryptBytes.Length);
            }
            finally
            {
                if (cryptoStream != null)
                    cryptoStream.Close();

                if (memoryStream != null)
                    memoryStream.Close();

                if (rm != null)
                    rm.Clear();
            }

            return decryptedData;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="clearText"></param>
        /// <param name="symmKey"></param>
        /// <returns></returns>
        public string EncryptString2(string clearText, string symmKey)
        {
            string encryptedData = "";

            RijndaelManaged rm = null;
            MemoryStream memoryStream = null;
            CryptoStream cryptoStream = null;

            try
            {
                string salt = symmKey.Substring(0, 16);
                //Generate a Key based on a Password, Salt and HMACSHA1 pseudo-random number generator 
                Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(symmKey, Encoding.Unicode.GetBytes(salt));

                //Create algorithm with 256 bit key and 128-bit block size 
                rm = new RijndaelManaged();
                rm.KeySize = 256;
                rm.BlockSize = 128;
                rm.Key = rfc2898.GetBytes(rm.KeySize / 8);
                rm.IV = rfc2898.GetBytes(rm.BlockSize / 8);

                //Create Memory and Crypto Streams 
                memoryStream = new MemoryStream();
                cryptoStream = new CryptoStream(memoryStream, rm.CreateEncryptor(), CryptoStreamMode.Write);

                //Encrypt Data 
                byte[] data = Encoding.Unicode.GetBytes(clearText);
                cryptoStream.Write(data, 0, data.Length);
                cryptoStream.FlushFinalBlock();

                //Return Base 64 String 
                encryptedData = Convert.ToBase64String(memoryStream.ToArray());
            }
            finally
            {
                if (cryptoStream != null)
                    cryptoStream.Close();

                if (memoryStream != null)
                    memoryStream.Close();

                if (rm != null)
                    rm.Clear();
            }

            return encryptedData;
        }

        /// <summary>
        /// Deprecated - padding problems
        /// </summary>
        /// <param name="InputText"></param>
        /// <param name="SymmKey"></param>
        /// <returns></returns>
        //public string EncryptString(string InputText, string SymmKey)
        //{
        //    string EncryptedData = "";

        //    RijndaelManaged RijndaelCipher = new RijndaelManaged();

        //    byte[] PlainText = System.Text.Encoding.Unicode.GetBytes(InputText);
        //    byte[] Salt = Encoding.ASCII.GetBytes(SymmKey.Length.ToString());

        //    //This class uses an extension of the PBKDF1 algorithm defined in the PKCS#5 v2.0 
        //    //standard to derive bytes suitable for use as key material from a password. 
        //    //The standard is documented in IETF RRC 2898.

        //    PasswordDeriveBytes SecretKey = new PasswordDeriveBytes(SymmKey, Salt);
        //    //Creates a symmetric encryptor object. 
        //    ICryptoTransform Encryptor = RijndaelCipher.CreateEncryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16));
        //    MemoryStream memoryStream = new MemoryStream();
        //    //Defines a stream that links data streams to cryptographic transformations
        //    CryptoStream cryptoStream = new CryptoStream(memoryStream, Encryptor, CryptoStreamMode.Write);
        //    cryptoStream.Write(PlainText, 0, PlainText.Length);
        //    //Writes the final state and clears the buffer
        //    cryptoStream.FlushFinalBlock();
        //    byte[] CipherBytes = memoryStream.ToArray();
        //    memoryStream.Close();
        //    cryptoStream.Close();
        //    EncryptedData = Convert.ToBase64String(CipherBytes);

        //    return EncryptedData;
        //}

        /// <summary>
        /// Deprecated - padding problems
        /// </summary>
        /// <param name="InputText"></param>
        /// <param name="SymmKey"></param>
        /// <returns></returns>
        //public string DecryptString(string InputText, string SymmKey)
        //{
        //    string DecryptedData = "";

        //    RijndaelManaged RijndaelCipher = new RijndaelManaged();
        //    byte[] EncryptedData = Convert.FromBase64String(InputText);
        //    byte[] Salt = Encoding.ASCII.GetBytes(SymmKey.Length.ToString());
        //    //Create the decryption key
        //    PasswordDeriveBytes SecretKey = new PasswordDeriveBytes(SymmKey, Salt);
        //    //Create a symmetric Rijndael decryptor
        //    ICryptoTransform Decryptor = RijndaelCipher.CreateDecryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16));
        //    MemoryStream memoryStream = new MemoryStream(EncryptedData);
        //    //Create the cryptographic stream for decryption
        //    CryptoStream cryptoStream = new CryptoStream(memoryStream, Decryptor, CryptoStreamMode.Read);
        //    byte[] PlainText = new byte[EncryptedData.Length];
        //    int DecryptedCount = cryptoStream.Read(PlainText, 0, PlainText.Length);
        //    memoryStream.Close();
        //    cryptoStream.Close();
        //    //Converting to string
        //    DecryptedData = Encoding.Unicode.GetString(PlainText, 0, DecryptedCount);

        //    return DecryptedData;
        //}


        # endregion


        # region Conversion

        /// <summary>
        /// converts base64 string to byte array
        /// </summary>
        /// <param name="inputStr"></param>
        /// <returns></returns>
        public byte[] Base64StringToByte(string inputStr)
        {
            byte[] buffer = Convert.FromBase64String(inputStr);
            return buffer;
        }

        /// <summary>
        /// converts an input string to a UTF8 byte array
        /// </summary>
        /// <param name="inputStr"></param>
        /// <returns></returns>
        public byte[] StringToByte(string inputStr)
        {
            //byte[] buffer = System.Text.Encoding.ASCII.GetBytes(inputStr);
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(inputStr);

            return buffer;
        }

        /// <summary>
        /// converts a byte array to a base 64 string
        /// </summary>
        /// <param name="inputByte"></param>
        /// <returns></returns>
        public string ByteToBase64String(byte[] inputByte)
        {
            //string asciiStr = System.Text.Encoding.ASCII.GetString(inputByte);
            //string utf8Str = System.Text.Encoding.UTF8.GetString(inputByte);
            return Convert.ToBase64String(inputByte);
        }

        /// <summary>
        /// converts byte array to UTF8 string
        /// </summary>
        /// <param name="inputByte"></param>
        /// <returns></returns>
        public string ByteToString(byte[] inputByte)
        {
            //string asciiStr = System.Text.Encoding.ASCII.GetString(inputByte);
            string utf8Str = System.Text.Encoding.UTF8.GetString(inputByte);
            return utf8Str;
        }

        # endregion

    }
}
